package com.aizuda.easyManagerTool.service.server.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.aizuda.easy.security.domain.Rep;
import com.aizuda.easy.security.domain.Req;
import com.aizuda.easyManagerTool.domain.dto.PageDTO;
import com.aizuda.easyManagerTool.domain.dto.server.ServerScriptFindDTO;
import com.aizuda.easyManagerTool.domain.dto.terminal.SSHInfoDTO;
import com.aizuda.easyManagerTool.domain.entity.server.ServerScriptCommandEntity;
import com.aizuda.easyManagerTool.domain.entity.server.ServerScriptEntity;
import com.aizuda.easyManagerTool.domain.entity.server.ServerScriptLogEntity;
import com.aizuda.easyManagerTool.domain.vo.PageVO;
import com.aizuda.easyManagerTool.domain.vo.server.ServerCompleteVO;
import com.aizuda.easyManagerTool.domain.vo.server.ServerScriptFindVO;
import com.aizuda.easyManagerTool.domain.vo.setting.SettingUserVO;
import com.aizuda.easyManagerTool.domain.vo.socket.SocketMessageVO;
import com.aizuda.easyManagerTool.mapper.server.ServerMapper;
import com.aizuda.easyManagerTool.mapper.server.ServerScriptMapper;
import com.aizuda.easyManagerTool.service.server.ServerScriptCommandService;
import com.aizuda.easyManagerTool.service.server.ServerScriptLogService;
import com.aizuda.easyManagerTool.service.server.ServerScriptService;
import com.aizuda.easyManagerTool.service.socket.impl.SocketSessionManager;
import com.aizuda.easyManagerTool.service.terminal.impl.TerminalSessionManager;
import com.aizuda.easyManagerTool.util.AssertUtil;
import com.aizuda.easyManagerTool.util.SSHManagerUtil;
import com.aizuda.easyManagerTool.util.ThreadPoolUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.Session;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;


@Service
public class ServerScriptServiceImpl extends ServiceImpl<ServerScriptMapper, ServerScriptEntity> implements ServerScriptService {

    @Resource
    private ServerScriptMapper serverScriptMapper;
    @Resource
    private ServerScriptCommandService serverScriptCommandService;
    @Resource
    private ServerMapper serverMapper;
    @Resource
    private ServerScriptLogService serverScriptLogService;

    private ThreadPoolUtil executorService = new ThreadPoolUtil("脚本管理执行-");

    @Override
    public Rep<PageVO<ServerScriptFindVO>> find(Req<PageDTO<ServerScriptFindDTO>, SettingUserVO> req) {
        PageDTO<ServerScriptFindDTO> pageDTO = req.getData();
        Integer roleId = req.getUser().getRoleId();
        Page<ServerScriptFindDTO> page = new Page<ServerScriptFindDTO>(pageDTO.getCurrent(), pageDTO.getSize());
        // 查询 唯一条件是要根据 权限查询
        ServerScriptFindDTO serverScriptFindDTO = pageDTO.getData() ;
        IPage<ServerScriptFindVO> serverListEntrtyIPage = serverScriptMapper.find(page, ObjectUtil.isEmpty(serverScriptFindDTO)? new ServerScriptFindDTO()
                :serverScriptFindDTO,roleId.toString());
        PageVO<ServerScriptFindVO> pageVO = new PageVO<ServerScriptFindVO>(pageDTO)
                .setTotal(serverListEntrtyIPage.getTotal())
                .setRecords(serverListEntrtyIPage.getRecords());
        return Rep.ok(pageVO);
    }

    @Transactional
    @Override
    public Rep<ServerScriptFindVO> edit(Req<ServerScriptFindDTO, SettingUserVO> req) {
        ServerScriptFindDTO data = req.getData();
        AssertUtil.objIsNull(data.getServerId(),"请选择服务器");
        AssertUtil.objIsNull(data.getScriptName(),"请输入脚本名称");
        if(ObjectUtil.isEmpty(data.getId())){
            data.setScriptHook(UUID.randomUUID().toString().replaceAll("-", ""));
        }
        saveOrUpdate(data);
        data.getCommands().forEach(i -> i.setScriptId(data.getId()));
        serverScriptCommandService.saveOrUpdates(data.getCommands());
        return Rep.ok();
    }

    @Transactional
    @Override
    public Rep<ServerScriptFindVO> del(Req<ServerScriptFindDTO, SettingUserVO> req) {
        ServerScriptFindDTO data = req.getData();
        SettingUserVO user = req.getUser();
        AssertUtil.objIsNull(data.getId(), "数据错误");
        serverScriptMapper.deleteById(data.getId());
        serverScriptCommandService.mapper().delByScriptId(data.getId());
        serverScriptLogService.mapper().delByScriptId(data.getId());
        return Rep.ok();
    }

    @Override
    public Rep<List<ServerScriptCommandEntity>> commandFind(Req<ServerScriptFindDTO, SettingUserVO> req) {
        ServerScriptFindDTO data = req.getData();
        AssertUtil.objIsNull(data.getId(), "数据错误");
        List<ServerScriptCommandEntity> list = serverScriptCommandService.mapper().findByScriptId(data.getId());
        return Rep.ok(list);
    }

    @Override
    public Rep<ServerScriptCommandEntity> commandDel(Req<ServerScriptCommandEntity, SettingUserVO> req) {
        ServerScriptCommandEntity data = req.getData();
        AssertUtil.objIsNull(data.getId(), "数据错误");
        serverScriptCommandService.mapper().deleteById(data.getId());
        return Rep.ok();
    }

    @Override
    public Rep<ServerScriptFindVO> exec(Req<ServerScriptFindDTO, SettingUserVO> req) {
        ServerScriptFindDTO data = req.getData();
        SettingUserVO user = req.getUser();
        AssertUtil.objIsNull(data.getId(), "数据错误");
        ServerScriptEntity serverScriptEntity = serverScriptMapper.selectById(data.getId());
        execScript(serverScriptEntity, user,0);
        return Rep.ok();
    }

    @Override
    public Rep<ServerScriptFindVO> hook(String hookId, Map<String, Object> map) {
        AssertUtil.objIsNull(hookId, "web钩子参数不正确");
        ServerScriptFindVO serverScriptFindVO = serverScriptMapper.findByHookId(hookId);
        AssertUtil.objIsNull(serverScriptFindVO, "未找到web钩子");
        execScript(serverScriptFindVO, null,1);
        return Rep.ok();
    }

    @Override
    public Rep<List<ServerScriptLogEntity>> logDel(Req<ServerScriptLogEntity, SettingUserVO> req) {
        ServerScriptLogEntity data = req.getData();
        AssertUtil.objIsNull(data.getId(), "数据错误");
        serverScriptLogService.mapper().deleteById(data.getId());
        return Rep.ok();
    }

    @Override
    public Rep<List<ServerScriptLogEntity>> logFind(Req<ServerScriptLogEntity, SettingUserVO> req) {
        ServerScriptLogEntity data = req.getData();
        AssertUtil.objIsNull(data.getScriptId(), "数据错误");
        List<ServerScriptLogEntity> serverScriptLogEntities = serverScriptLogService.mapper().findAll(data.getScriptId());
        return Rep.ok(serverScriptLogEntities);
    }

    private void execScript(ServerScriptEntity serverScriptEntity,SettingUserVO user,Integer logTrigger){
        List<ServerScriptCommandEntity> byScriptId = serverScriptCommandService.mapper().findByScriptId(serverScriptEntity.getId());
        byScriptId = byScriptId.stream().sorted(Comparator.comparing(ServerScriptCommandEntity::getSscOrder)).collect(Collectors.toList());
        ServerCompleteVO serverCompleteVO = serverMapper.findById(serverScriptEntity.getServerId());
        SSHInfoDTO sshInfoDTO = BeanUtil.copyProperties(serverCompleteVO, SSHInfoDTO.class);
        TerminalSessionManager.Manager manager = new TerminalSessionManager.Manager();
        List<ServerScriptCommandEntity> finalByScriptId = byScriptId;
        ServerScriptLogEntity serverScriptLogEntity = new ServerScriptLogEntity();
        serverScriptLogEntity.setScriptId(serverScriptEntity.getId());
        serverScriptLogEntity.setLogTrigger(logTrigger);
        serverScriptLogEntity.setTenantId(serverScriptEntity.getTenantId());
        serverScriptLogEntity.setLogStatus(Boolean.TRUE);
        executorService.execute(() -> {
            Session session = null;
            ChannelExec channelExec = null;
            StringBuilder sb = new StringBuilder();
            try {
                session = SSHManagerUtil.setSession(manager, sshInfoDTO);
                for (ServerScriptCommandEntity serverScriptCommandEntity : finalByScriptId) {
                    String command = serverScriptCommandEntity.getSscCommand();
                    if(command.startsWith("cd /") || command.startsWith("cd ./") || command.startsWith("cd ..")){}else {
                        command = "cd "+serverScriptEntity.getScriptGlobalPath()+" && "+command;
                    }
                    String str = "\r\n执行：" + command + "\r\n";
                    if(ObjectUtil.isNotEmpty(user)) {
                        SocketSessionManager.sendToUser(user, buildSocketMessageVO("server:script:" + serverScriptEntity.getId(),str ));
                    }
                    sb.append(str);
                    channelExec = SSHManagerUtil.setChannelExec(session, manager,command);
                    channelExec.setErrStream(System.err);
                    channelOutStream(channelExec, user, serverScriptEntity.getId(),sb);
                    channelExec.disconnect();
                }
            }catch(Exception e){
                serverScriptLogEntity.setLogStatus(Boolean.FALSE);
                try {
                    if(ObjectUtil.isNotEmpty(user)) {
                        SocketSessionManager.sendToUser(user, buildSocketMessageVO("server:script:" + serverScriptEntity.getId(), e.getMessage()));
                    }
                } catch (IOException ex) {
                    throw new RuntimeException(ex);
                }
                e.printStackTrace();
            }finally {
                serverScriptLogEntity.setScriptLog(sb.toString());
                serverScriptLogService.mapper().insert(serverScriptLogEntity);
                manager.close();
            }
        });
    }

    private SocketMessageVO<String> socketMessage = new SocketMessageVO<>();
    private void channelOutStream(ChannelExec channel,SettingUserVO user,Integer id,StringBuilder sb) throws Exception {
        InputStream inputStream = channel.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        channel.setErrStream(System.err);
        // Read the output line by line
        String line;
        while ((line = reader.readLine()) != null) {
            line = line + " \r\n";
            sb.append(line);
            if(ObjectUtil.isNotEmpty(user)) {
                SocketSessionManager.sendToUser(user, buildSocketMessageVO("server:script:" + id, line));
            }
        }
        inputStream.close();
    }

    private String buildSocketMessageVO(String type,String msg){
        SocketMessageVO<String> socketMessageVO = BeanUtil.copyProperties(socketMessage, SocketMessageVO.class);
        socketMessageVO.setObj(msg);
        socketMessageVO.setType(type);
        return JSONUtil.toJsonStr(socketMessageVO);
    }

}
